#ifndef URCONTROLLINUX_HPP
#define URCONTROLLINUX_HPP

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <netdb.h>

#include <algorithm>

class URControlLinux
{
protected:
    static void error(const char *msg)
    {
        perror(msg);
        exit(1);
    }

    static void startserver(uint16_t portno, std::string &result)
    {
        int sockfd, newsockfd;
        socklen_t clilen;
        char buffer[256];
        struct sockaddr_in serv_addr, cli_addr;
        int n;

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
            error("ERROR opening socket");
        bzero((char *) &serv_addr, sizeof(serv_addr));

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(portno);

        const int       optVal = 1;
        const socklen_t optLen = sizeof(optVal);

        if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &optVal, optLen)!=0)
        {
            error("ERROR on setsockopt");
        }

        if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        {
            error("ERROR on binding");
        }

        listen(sockfd,5);
        clilen = sizeof(cli_addr);

        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0)
            error("ERROR on accept");
        bzero(buffer,256);


#if 0
        n = write(newsockfd,"I got hhhh message\n",19);

        if (n < 0) {
            error("ERROR writing to socket");
        }

        n = write(newsockfd,"1.2,2.2,3.21221224\n",19);

        if (n < 0) {
            error("ERROR writing to socket");
        }
#endif

        n = read(newsockfd,buffer,255);
        if (n < 0) {
            error("ERROR reading from socket");
        }

        //        printf("Here is the message: %s\n",buffer);

        result=std::string(buffer, buffer+n);

        close(newsockfd);

        close(sockfd);
    }

    static void startclient(uint16_t portno, const char *hostname, const char *buffer)
    {
        int sockfd, n;
        struct sockaddr_in serv_addr;
        struct hostent *server;

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
            error("ERROR opening socket");
        server = gethostbyname(hostname);
        if (server == NULL)
        {
            fprintf(stderr,"ERROR, no such host\n");
            exit(0);
        }
        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
        serv_addr.sin_port = htons(portno);
        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
            error("ERROR connecting");

        n = write(sockfd,buffer,strlen(buffer));
        if (n < 0)
            error("ERROR writing to socket");
#if 0
        bzero(buffer,256);
        n = read(sockfd,buffer,255);
        if (n < 0)
            error("ERROR reading from socket");
#endif
        //        printf("%s\n",buffer);
        close(sockfd);
    }


    //https://www.universal-robots.com/how-tos-and-faqs/how-to/ur-how-tos/modbus-server-16377/
    static void ReadModbusRegister(const char *ipUR, unsigned char registerNum, unsigned short startAddress, short *registerValue, uint16_t modbusPort=502)
    {
        int sockfd, n;
        struct sockaddr_in serv_addr;
        struct hostent *server;

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
            error("ERROR opening socket");
        server = gethostbyname(ipUR);
        if (server == NULL)
        {
            fprintf(stderr,"ERROR, no such host\n");
            exit(0);
        }
        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
        serv_addr.sin_port = htons(modbusPort);
        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
            error("ERROR connecting");

        unsigned char cmd[12];
        cmd[0]=0;
        cmd[1]=1;
        cmd[2]=0;
        cmd[3]=0;
        cmd[4]=0;
        cmd[5]=6;
        cmd[6]=0;
        cmd[7]=3;
        cmd[8]=startAddress>>8;
        cmd[9]=startAddress;
        cmd[10]=0;
        cmd[11]=registerNum;

        n = write(sockfd,cmd,12);
        if (n < 0)
            error("ERROR writing to socket");

        unsigned int resLength=9+2*registerNum;
        unsigned char buffer[resLength];
        bzero(buffer,resLength);
        n = read(sockfd,buffer,resLength);
        if (n < 0)
            error("ERROR reading from socket");

        //        printf("%s\n",buffer);
        close(sockfd);

        unsigned char *rss=new unsigned char[2*registerNum];
        std::reverse_copy(buffer+9,buffer+resLength,rss);
        short *rsss=(short*)rss;
        std::reverse_copy(rsss,rsss+registerNum,registerValue);
        delete rss;
    }


};




#endif // URCONTROLLINUX_HPP

